<template>
  <view
    class="month-calendar"
    :style="{
      position: sticky ? 'sticky' : 'static',
      top: sticky ? top + 'rpx' : null,
      zIndex: sticky ? 100 : null,
      '--color': color ? color : '#757575',
    }"
  >
    <view
      class="calendar-box"
      :style="{
        background: bgColor,
        '--watermark': `'${currentYear}年${
          currentMonth > 9 ? currentMonth : '0' + currentMonth
        }月'`,
        '--showWatermark': watermark ? 'block' : 'none',
      }"
    >
      <view
        v-if="mode === 'week'"
        class="calendar-heander"
        :style="{
          justifyContent: align == 'center' ? 'center' : 'space-between',
        }"
      >
        <template v-if="align == 'center'">
          <view class="before" />
          <view class="title">{{ timeStr }}</view>
          <view class="after" />
        </template>
        <template v-if="align == 'left'">
          {{ timeStr }}
        </template>
        <view
          class="back-today"
          @click="goback"
          v-if="showBack && isToday && align === 'left'"
        >
          回到今天
        </view>
      </view>
      <view v-if="mode === 'month'" class="calendar-heander-month">
        <view class="arrow" @click="daysPre" />
        {{ currentYear }}年{{  currentMonth > 9 ? currentMonth : '0' + currentMonth }}月
        <view class="arrow" @click="daysNext" />
      </view>
      <view class="calendar-weeks">
        <view
          class="calendar-week"
          v-for="(week, index) in weeks"
          :key="index"
          :style="{
            color: color ? color : '#34485e',
          }"
        >
          {{ week }}
        </view>
      </view>
      <view class="calendar-content">
        <swiper
          class="calendar-swiper"
          :style="{
            width: '100%',
            height: sheight,
          }"
          :indicator-dots="false"
          :autoplay="false"
          :duration="duration"
          :current="current"
          @change="changeSwp"
          :circular="true"
        >
          <swiper-item class="calendar-item" v-for="sitem in swiper" :key="sitem">
            <view class="calendar-days">
              <template v-if="sitem === current">
                <view
                  class="calendar-day"
                  v-for="(item, index) in days"
                  :key="index"
                  :class="{
                    'day-hidden': !item.show,
                  }"
                  @click="clickItem(item)"
                >
                  <view
                    class="date"
                    :class="[
                      item.isToday ? todayClass : '',
                      item.fullDate === selectedDate ? checkedClass : '',
                    ]"
                  >
                    {{ item.time.getDate() }}
                  </view>
                  <view class="dot-show" v-if="item.info" :style="dotStyle"> </view>
                </view>
              </template>
              <template v-else>
                <template v-if="current - sitem === 1 || current - sitem === -2">
                  <view
                    class="calendar-day"
                    v-for="(item, index) in predays"
                    :key="index"
                    :class="{
                      'day-hidden': !item.show,
                    }"
                  >
                    <view class="date" :class="[item.isToday ? todayClass : '']">
                      {{ item.time.getDate() }}
                    </view>
                  </view>
                </template>
                <template v-else>
                  <view
                    class="calendar-day"
                    v-for="(item, index) in nextdays"
                    :key="index"
                    :class="{
                      'day-hidden': !item.show,
                    }"
                  >
                    <view class="date" :class="[item.isToday ? todayClass : '']">
                      {{ item.time.getDate() }}
                    </view>
                  </view>
                </template>
              </template>
            </view>
          </swiper-item>
        </swiper>
        <view v-if="more" class="mode-change">
          <view
            :style="{
              transform: `rotate(${weekMode ? 90 : 270}deg)`,
            }"
            @click="changeMode"
          >
            》
          </view>
        </view>
      </view>
    </view>
    <slot>
      <!-- 默认插槽 -->
    </slot>
    <view
      class="cal-button"
      v-if="showBack && isToday && align === 'center'"
      @click="goback"
    >
      今
    </view>
  </view>
</template>
<script setup>
import { ref, computed, watch, onMounted } from "vue";
import { gegerateDates, dateEqual, formatDate } from "./generateDates.js";

const weeks = ref(["一", "二", "三", "四", "五", "六", "日"]);
const current = ref(1);
const currentYear = ref("");
const currentMonth = ref("");
const currentDate = ref("");
const days = ref([]);
const weekMode = ref(true);
const swiper = ref([0, 1, 2]);
const selectedDate = ref(formatDate(new Date(), "yyyy-MM-dd"));

const props = defineProps({
  // 滚动速度ms
  duration: {
    type: Number,
    default: 500,
  },
  // 打点日期列表
  dotList: {
    type: Array,
    default: () => [],
  },
  // 显示返回今日
  showBack: {
    type: Boolean,
    default: false,
  },
  // 自定义今日样式
  todayClass: {
    type: String,
    default: "is-today",
  },
  // 选中日期的样式
  checkedClass: {
    type: String,
    default: "is-checked",
  },
  // 是否吸顶
  sticky: {
    type: Boolean,
    default: false,
  },
  // 吸顶距离(开启吸顶生效)
  top: {
    type: [String, Number],
    default: 0,
  },
  // 日历视图类型
  mode: {
    type: String,
    default: "week",
  },
  // 显示更多按钮
  more: {
    type: Boolean,
    default: true,
  },
  // 标记日期的自定义样式
  dotStyle: {
    type: Object,
    default() {
      return {
        background: "#4EA770",
      };
    },
  },
  // 背景色
  bgColor: {
    type: String,
    default: "#fff",
  },
  // 文本颜色
  color: {
    type: String,
    default: "",
  },
  // 对齐方式
  align: {
    type: String,
    default: "left",
  },
  // 显示水印
  watermark: {
    type: Boolean,
    default: false,
  },
});

const emit = defineEmits(["days-change", "selected-change"]);

watch(
  props,
  (val) => {
    if (val.dotList) {
      setTimeout(() => {
        let list = days.value.slice(0) || [];
        list = list?.map(i => {
          return { ...i, info: '' }
        });
        val.dotList.forEach((d) => {
          const index = list.findIndex((i) => i.fullDate == d);
          if (index >= 0) list[index].info = d;
        });
        days.value = list;
      }, 0);
    }
  },
  { deep: true, immediate: true }
);

onMounted(() => {
  if (props.mode === "month") {
    weekMode.value = false;
  } else {
    weekMode.value = true;
  }
  initDate();
});

// 日历高度
const sheight = computed(() => {
  // 判断该月有多少天
  let h = "80rpx";
  if (!weekMode.value) {
    const d = new Date(currentYear.value, currentMonth.value, 0);
    const days = d.getDate(); // 判断本月有多少天
    let day = new Date(d.setDate(1)).getDay();
    if (day === 0) day = 7;
    const pre = 8 - day;
    const rows = Math.ceil((days - pre) / 7) + 1;
    h = 80 * rows + "rpx";
  }
  return h;
});

// 当前年月
const timeStr = computed(() => {
  let choose = new Date(selectedDate.value);
  const y = choose.getFullYear();
  const m =
    choose.getMonth() + 1 <= 9 ? `0${choose.getMonth() + 1}` : choose.getMonth() + 1;
  const d = choose.getDate() <= 9 ? `0${choose.getDate()}` : choose.getDate();
  return props.mode === "month"
    ? `${y}年${m}月`
    : props.align == "center"
    ? `${y}-${m}-${d}`
    : `${y} | ${m}.${d}`;
});

// 上个月天数
const predays = computed(() => {
  let pres = [];
  if (weekMode.value) {
    const d = new Date(currentYear.value, currentMonth.value - 1, currentDate.value);
    d.setDate(d.getDate() - 7);
    pres = gegerateDates(d, "week");
  } else {
    const d = new Date(currentYear.value, currentMonth.value - 2, 1);
    pres = gegerateDates(d, "month");
  }
  return pres;
});

// 下个月天数
const nextdays = computed(() => {
  let nexts = [];
  if (weekMode.value) {
    const d = new Date(currentYear.value, currentMonth.value - 1, currentDate.value);
    d.setDate(d.getDate() + 7);
    nexts = gegerateDates(d, "week");
  } else {
    const d = new Date(currentYear.value, currentMonth.value, 1);
    nexts = gegerateDates(d, "month");
  }
  return nexts;
});

// 是否是今天
const isToday = computed(() => {
  const d = new Date();
  const day = formatDate(d, "yyyy-MM-dd");
  return day != selectedDate.value;
});

// 切换月份/周
const changeSwp = (e) => {
  const pre = current.value;
  const cur = e.detail.current;
  /* 根据前一个减去目前的值我们可以判断是下一个月/周还是上一个月/周
   *cur - pre === 1, -2时是下一个月/周
   *cur -pre === -1, 2时是上一个月或者上一周
   */
  current.value = cur;
  console.log(pre, cur);
  if (cur - pre === 1 || cur - pre === -2) {
    daysNext();
  } else {
    daysPre();
  }
};
// 初始化日历的方法
const initDate = (cur) => {
  let date = "";
  if (cur) {
    date = new Date(cur);
  } else {
    date = new Date();
  }
  currentDate.value = date.getDate();
  currentYear.value = date.getFullYear();
  currentMonth.value = date.getMonth() + 1;
  days.value = [];
  let list = [];
  if (weekMode.value) {
    list = gegerateDates(date, "week");
  } else {
    list = gegerateDates(date, "month");
  }
  list.forEach((day) => {
    const dot = props.dotList.find((i) => {
      return dateEqual(i, day.fullDate);
    });
    if (dot) day.info = dot;
  });
  days.value = list;
  //  派发事件,时间发生改变
  let obj = {
    start: "",
    end: "",
  };
  if (weekMode.value) {
    obj.start = days.value[0].time;
    obj.end = days.value[6].time;
  } else {
    const start = new Date(currentYear.value, currentMonth.value - 1, 1);
    const end = new Date(currentYear.value, currentMonth.value, 0);
    obj.start = start;
    obj.end = end;
  }
  emit("days-change", obj);
};
//  上一个
const daysPre = () => {
  if (weekMode.value) {
    const d = new Date(currentYear.value, currentMonth.value - 1, currentDate.value);
    d.setDate(d.getDate() - 7);
    initDate(d);
  } else {
    const d = new Date(currentYear.value, currentMonth.value - 2, 1);
    initDate(d);
  }
};
//  下一个
const daysNext = () => {
  if (weekMode.value) {
    const d = new Date(currentYear.value, currentMonth.value - 1, currentDate.value);
    d.setDate(d.getDate() + 7);
    initDate(d);
  } else {
    const d = new Date(currentYear.value, currentMonth.value, 1);
    initDate(d);
  }
};
// 切换模式
const changeMode = () => {
  const premode = weekMode.value;
  let isweek = false;
  if (premode) {
    isweek = !!days.value.find((i) => i.fullDate === selectedDate.value);
  }
  weekMode.value = !weekMode.value;
  let d = new Date(currentYear.value, currentMonth.value - 1, currentDate.value);
  const sel = new Date(selectedDate.value.replace("-", "/").replace("-", "/"));
  const isMonth =
    sel.getFullYear() === currentYear.value && sel.getMonth() + 1 === currentMonth.value;
  if ((selectedDate.value && isMonth) || isweek) {
    d = new Date(selectedDate.value.replace("-", "/").replace("-", "/"));
  }
  initDate(d);
};
// 点击日期
const clickItem = (e) => {
  selectedDate.value = e.fullDate;
  emit("selected-change", e.fullDate);
};
// 回到今天
const goback = () => {
  const d = new Date();
  selectedDate.value = formatDate(d, "yyyy-MM-dd");
  emit("selected-change", formatDate(d, "yyyy-MM-dd"));
  initDate(d);
};
</script>

<style lang="scss" scoped>
.month-calendar {
  --watermark: "";
  --showWatermark: none;
  --color: #757575;
  width: 100%;
  height: auto;
  background: #fff;
  overflow: hidden;
  box-shadow: 0 0 20rpx 2rpx rgba(117, 117, 117, 0.16);
  border-radius: 20rpx;
  margin-bottom: 30rpx;
  .calendar-box {
    position: relative;
    &::after {
      content: var(--watermark);
      display: var(--showWatermark);
      position: absolute;
      top: 50%;
      left: 0;
      right: 0;
      text-align: center;
      transform: translateY(-50%);
      color: rgba(0, 0, 0, 0.2); /* 设置水印文字颜色和透明度 */
      font-size: 100rpx;
      pointer-events: none;
    }
  }
  .calendar-heander {
    height: 80rpx;
    line-height: 80rpx;
    font-size: 30rpx;
    color: #6b7897;
    font-size: 28rpx;
    padding: 0 20rpx;
    display: flex;
    justify-content: space-between;
    align-items: center;
    .title {
      color: var(--color);
      padding: 0 10rpx;
      font-size: 32rpx;
      font-weight: 600;
    }
    .before {
      width: 12rpx;
      height: 24rpx;
      margin-right: 10rpx;
      display: inline-block;
      background-color: var(--color);
      position: relative;
      border-radius: 8rpx;
      &::before {
        content: "";
        position: absolute;
        left: -20rpx;
        top: 8rpx;
        width: 12rpx;
        height: 16rpx;
        display: inline-block;
        border-radius: 8rpx;
        background-color: var(--color);
      }
    }
    .after {
      width: 12rpx;
      height: 24rpx;
      margin-left: 10rpx;
      display: inline-block;
      background-color: var(--color);
      position: relative;
      border-radius: 8rpx;
      &::before {
        content: "";
        position: absolute;
        left: 20rpx;
        top: 8rpx;
        width: 12rpx;
        height: 16rpx;
        display: inline-block;
        border-radius: 8rpx;
        background-color: var(--color);
      }
    }
  }
  .calendar-heander-month {
    height: 120rpx;
    line-height: 120rpx;
    font-size: 30rpx;
    color: #6b7897;
    font-size: 28rpx;
    padding: 0 20rpx;
    display: flex;
    justify-content: center;
    align-items: center;
    .arrow {
      width: 42rpx;
      height: 42rpx;
      border-radius: 50%;
      background-color: #f7f8fc;
      display: flex;
      justify-content: center;
      align-items: center;
      &:active {
        background-color: #f5f5f5;
      }
      &:first-child {
        margin-right: 60rpx;
        &::before {
          content: "";
          width: 16rpx;
          height: 16rpx;
          border-top: 0 solid #4ea770;
          border-right: 0 solid #4ea770;
          border-left: 4rpx solid #4ea770;
          border-bottom: 4rpx solid #4ea770;
          transform: rotate(45deg);
          margin-left: 6rpx;
        }
      }
      &:last-child {
        margin-left: 60rpx;
        &::after {
          content: "";
          width: 16rpx;
          height: 16rpx;
          border-top: 4rpx solid #4ea770;
          border-right: 4rpx solid #4ea770;
          border-left: 0 solid #4ea770;
          border-bottom: 0 solid #4ea770;
          transform: rotate(45deg);
          margin-right: 6rpx;
        }
      }
    }
  }
  .calendar-weeks {
    width: 100%;
    display: flex;
    flex-flow: row nowrap;
    height: 60rpx;
    line-height: 60rpx;
    justify-content: center;
    align-items: center;
    .calendar-week {
      width: calc(100% / 7);
      height: 100%;
      font-size: 30rpx;
      text-align: center;
    }
  }
  swiper {
    width: 100%;
    height: 60rpx;
  }
  .calendar-content {
    min-height: 100rpx;
  }
  .calendar-swiper {
    min-height: 90rpx;
    transition: height ease-out 0.3s;
  }
  .calendar-item {
    margin: 0;
    padding: 0;
    height: 100%;
  }
  .calendar-days {
    display: flex;
    flex-flow: row wrap;
    width: 100%;
    height: 100%;
    overflow: hidden;
    font-size: 28rpx;
    .calendar-day {
      width: calc(100% / 7);
      height: 80rpx;
      text-align: center;
      display: flex;
      flex-flow: column nowrap;
      justify-content: flex-start;
      align-items: center;
    }
  }
  .day-hidden {
    visibility: hidden;
  }
  .mode-change {
    display: flex;
    justify-content: center;
    margin-top: 10rpx;
    view {
      color: #bdbdbd;
      font-size: 40rpx;
      transition: transform 0.3s;
      margin-bottom: 10rpx;
    }
  }
  .date {
    width: 60rpx;
    height: 60rpx;
    line-height: 60rpx;
    margin: 0 auto;
    font-size: 28rpx;
    border-radius: 60rpx;
    color: var(--color);
  }
  .is-today {
    border-radius: 50%;
    color: #4ea770;
  }
  .dot-show {
    margin-top: 6rpx;
    width: 12rpx;
    height: 12rpx;
    background: #c6c6c6;
    border-radius: 50%;
  }
  .is-checked {
    background: #4ea770;
    color: #ffffff;
  }
}
.cal-button {
  width: 80rpx;
  height: 80rpx;
  border-radius: 50%;
  font-size: 28rpx;
  display: flex;
  justify-content: center;
  align-items: center;
  color: var(--color);
  background-color: #4ea770;
  position: fixed;
  bottom: 200rpx;
  right: 40rpx;
}
</style>
